const toString = Object.prototype.toString

export function is(val: any, type: string) {
    return toString.call(val) === `[object ${type}]`
}

export function isDef(val: any) {
    return typeof val !== 'undefined'
}

export function isUnDef(val: any) {
    return !isDef(val)
}

export function isEmpty(val: any) {
    if (isArray(val) || isString(val)) {
        return val.length === 0
    }

    if (val instanceof Map || val instanceof Set) {
        return val.size === 0
    }

    if (isObject(val)) {
        return Object.keys(val).length === 0
    }

    return false
}

export function isSetObject(params: any) {
    return is(params, 'Set')
}

export function isObject(params: any) {
    return is(params, 'Object')
}

export function isDate(val: any) {
    return is(val, 'Date')
}

export function isNull(val: any) {
    return val === null
}

export function isNullAndUnDef(val: any) {
    return isUnDef(val) && isNull(val)
}

export function isNullOrUnDef(val: any) {
    return isUnDef(val) || isNull(val)
}

export function isNumber(val: any) {
    return is(val, 'Number')
}

export function isPromise(val: any) {
    return is(val, 'Promise') && isObject(val) && isFunction(val.then) && isFunction(val.catch)
}

export function isString(val: any) {
    return is(val, 'String')
}

export function isFunction(val: any) {
    return is(val, 'Function')
}

export function isBoolean(val: any) {
    return is(val, 'Boolean')
}

export function isRegExp(val: any) {
    return is(val, 'RegExp')
}

export function isArray<T = any>(val: any): val is Array<T> {
    return is(val, 'Array')
}
/* 空对象判断*/
export function isEmptyObject(obj: any) {
    return !Object.keys(obj).length
}
/* 为空判断*/
export function isNullOrEmpty(obj: any) {
    return (
        (obj !== 0 || obj !== '0') &&
        (obj === null || obj === undefined || obj === '' || obj === 'null' || typeof obj === 'undefined')
    )
}

/* 空数组对象判断*/
export function isEmptyArr(arr: any) {
    return isArray(arr) && arr.length === 0
}

// 该方法已弃用，后面把依赖的地方修改后会删除；深克隆请使用cloneDeep方法
export function deepCopy(obj: any) {
    const jsonData = JSON.stringify(obj)
    const newobj = JSON.parse(jsonData)
    return newobj
}

export function cloneDeep(obj: any) {
    // 如果当前数据不是对象类型，则不需要深拷贝，直接返回即可
    if (typeof obj !== 'object' || obj === null) {
        return obj
    }

    return JSON.parse(JSON.stringify(obj))
}

/**
 * 获取两个简单数组的差集
 * @param {Array} arr1
 * @param {Array} arr2
 */
export function getDifferenceArray(arr1, arr2) {
    if (isArray(arr1) && isArray(arr2)) {
        const set1 = new Set(arr1);
        const set2 = new Set(arr2);
        return Array.from(new Set([...set1].filter((x) => !set2.has(x))));
    }
    return [];
}

export function merge(source, other) {
    if (!isObject(source) || !isObject(other)) {
        return other === undefined ? source : other;
    }
    // 合并两个对象的 key，另外要区分数组的初始值为 []
    return Object.keys({
        ...source,
        ...other,
    }).reduce(
        (acc, key) => {
            // 递归合并 value
            source[key] = merge(source[key], other[key]);
            acc[key] = source[key];
            return acc;
        },
        Array.isArray(source) ? [] : {},
    );
}

/**
 * 处理页面跳转
 * @param {String} href 目标URL
 * @param $router
 * @param {String} target  跳转方式 _self（默认）当前页跳转， _blank为新页面打开
 * @param {String} routeType  跳转方式： auto（默认） -> 通过判断url中是否包括http判断是否使用a标签进行跳转；a ->强行a标签；router->强行router-link
 */
export function $handleRedirect(href: string, $router: any, target = '_self', routeType = 'auto') {
    const isAlink = (routeType === 'auto' && href.indexOf('http') > -1) || routeType === 'a' // 判断是否采用非router路由模式
    let targetUrl = { href } // 构建链接，形式同$router.resolve后的结果
    if (!isAlink) {
        targetUrl = $router.resolve(href) // 如果是router路由模式，通过resolve的方式构建链接用以兼容实现Vue router-link 模式下的新窗口链接（_blank）
    }
    window.open(targetUrl.href, target)
}

// 给数字添加千分位分隔符
export function addThousandsSeparatorToNumber(number) {
    const formatNumber = Number(number)

    // 如果当前值不能转成数字，则返回原值；否则对其添加千分位分隔符
    if (isNaN(formatNumber)) {
        return number
    } else {
        return formatNumber.toLocaleString()
    }
}

/**
 * 获取持久化信息
 * @param key 存储的key
 */
export function localGet(key: string) {
    try {
        const result = JSON.parse(localStorage.getItem(key) || '{}')
        if (result.hasOwnProperty('value')) {
            return result.value
        } else {
            return result
        }
    } catch (error) {
        return
    }
}

//判断某个数组中是否包含另一个数组/值
export function isContained<T = any>(target: T[], value: T[] | T) {
    if (!(target instanceof Array)) {
        return false
    }
    if (!(value instanceof Array)) {
        return target.includes(value)
    }
    if (target.length < value.length || value.length === 0 || target.length === 0) {
        return false
    }
    for (let i = 0; i < value.length; i++) {
        if (!target.includes(value[i])) {
            return false
        }
    }
    return true
}

interface BlockReference<T> {
    node: T | null
}
interface HandleNode<T> {
    (node: T): T | null
}
interface HandleLeafNode<T> {
    (node: T, reference: BlockReference<T>): void
}

// 通过handleNode递归逐个处理树形节点、handleLeafNode对叶子节点进行处理。data为需要递归的树结构
export function traversalTreeStructure<T extends { children?: T[]; [key: string]: any }>(
    data: Array<T>,
    handleNode: HandleNode<T> = node => ({ ...node, children: [] }),
    handleLeafNode?: HandleLeafNode<T>,
    childrenKey = 'children',
): Array<T> {
    if (!isArray(data)) {
        return []
    }
    const filterData: T[] = []
    for (let index = 0; index < data.length; index++) {
        // 创建引用对象，用于过滤叶子节点的实现
        const reference: BlockReference<T> = { node: null }
        reference.node = handleNode(data[index])
        if (isArray<T>(data[index][childrenKey])) {
            // 如果有children继续递归
            const result = traversalTreeStructure(data[index][childrenKey], handleNode, handleLeafNode, childrenKey)
            if (result.length && reference.node) {
                reference.node.children = result
            }
        } else {
            handleLeafNode && handleLeafNode(data[index], reference)
        }
        reference.node && filterData.push(reference.node)
    }
    return filterData
}
/**
 *  条件类型枚举
 *string
 * @export
 * @enum {string}
 */
export enum ConditionType {
  // 空
  IS_NULL = 'IS_NULL',
  // 非空
  IS_NOT_NULL = 'IS_NOT_NULL',
  // 属于
  BELONG = 'BELONG',
  // 不属于
  NOT_BELONG = 'NOT_BELONG',
  // 包含
  CONTAIN = 'CONTAIN',
  // 不包含
  NOT_CONTAIN = 'NOT_CONTAIN',
  // 开头是
  PRE_IS = 'PRE_IS',
  // 开头不是
  PRE_NOT_IS = 'PRE_NOT_IS',
  // 结尾是
  END_IS = 'END_IS',
  // 结尾不是
  END_NOT_IS ='END_NOT_IS',
  // 介于
  BETWEEN = 'BETWEEN',
  // 不介于
  NOT_BETWEEN =  'NOT_BETWEEN',
  // 等于
  EQUALS = 'EQUALS',
  // 不等于
  NOT_EQUALS = 'NOT_EQUALS',
  // 某个日期之前
  DATE_LT = 'DATE_LT',
  // 某个日期之后
  DATE_GT = 'DATE_GT',
  // 全等
  EQ = 'EQ',
  // 不全等
  NE = 'NE',
  // 大于
  GT = 'GT',
  // 小于
  LT = 'LT',
  // 大于等于
  GE = 'GE',
  // 小于等于
  LE = 'LE',
};
/**
 * 左值为null/undefined时，可以使用的条件类型
 */
const leftValueCanBeNull = [
  ConditionType.IS_NULL,
  ConditionType.IS_NOT_NULL,
  ConditionType.EQUALS,
  ConditionType.NOT_EQUALS,
  ConditionType.EQ,
  ConditionType.NE,
];

/**
 * 检查条件是否成立
 * @param leftValue
 * @param conditionType
 * @param rightValue
 * @returns
 */
export const checkCaseCondition = (leftValue: any, conditionType: ConditionType, rightValue: any) => {
  if (!conditionType) {
    return false;
  }
  // 如果当前值值不存在，且左值不能为null/undefined时 则不满足条件
  if (leftValue === undefined || leftValue === null) {
    if (!leftValueCanBeNull.includes(conditionType)) {
      return false;
    }
  }

  switch (conditionType) {
    case ConditionType.CONTAIN: // 包含
      return leftValue.indexOf(rightValue) > -1;
    case ConditionType.NOT_CONTAIN: // 不包含
      return leftValue.indexOf(rightValue) === -1;
    case ConditionType.IS_NULL: // 为空
      return !leftValue;
    case ConditionType.IS_NOT_NULL: // 非空
      return !!leftValue;
    case ConditionType.PRE_IS: // 开头是
      return leftValue.startsWith(rightValue);
    case ConditionType.PRE_NOT_IS: // 开头不是
      return !leftValue.startsWith(rightValue);
    case ConditionType.END_IS: // 结尾是
      return leftValue.endsWith(rightValue);
    case ConditionType.END_NOT_IS: // 结尾不是
      return !leftValue.endsWith(rightValue);
    case ConditionType.EQUALS: // 等于
      return leftValue == rightValue;
    case ConditionType.NOT_EQUALS: // 不等于
      return leftValue != rightValue;
    case ConditionType.GT: // 大于
      return leftValue > rightValue;
    case ConditionType.LT: // 小于
      return leftValue < rightValue;
    case ConditionType.EQ: // 全等
      return leftValue === rightValue;
    case ConditionType.NE: // 不全等
      return leftValue !== rightValue;
    case ConditionType.GE: // 大于等于
      return leftValue >= rightValue;
    case ConditionType.LE: // 小于等于
      return leftValue <= rightValue;
    default:
      return false;
  }
};
/**
 * 判断单元格是否可编辑
 * @param {*} columnsConfig
 * @param {*} rowData
 * @param {*} conditionValueMap
 * @returns
 */
export const judgeCellEditable = (columnsConfig, rowData, conditionValueMap) => {
  if (columnsConfig.rowEditConditions) {
    for (let index = 0; index < columnsConfig.rowEditConditions.length; index++) {
      const { condition, editable } = columnsConfig.rowEditConditions[index];
      const { conditionId, conditionKey, conditionType } = condition;
      // 如果条件满足，则使用该条件的值
      if (
        checkCaseCondition(
          rowData[conditionKey],
          conditionType,
          conditionValueMap[`${conditionId}__$__${conditionKey}`],
        )
      ) {
        // 返回该条件的可编辑状态
        return editable;
      }
    }
  }
  return columnsConfig.rowEdit;
};

/**
 * 获取单元格内容样式
 * @param {*} contentConfig
 * @param {*} rowData
 * @param {*} conditionValueMap
 * @returns
 */
export function generateContentStyle(contentConfig, rowData, conditionValueMap) {
    if (contentConfig.conditionStyle) {
        for (let index = 0; index < contentConfig.conditionStyle.length; index++) {
        const { condition, style } = contentConfig.conditionStyle[index];
        // 如果没有条件配置或样式配置，则跳过该条件样式
        if (!condition || !style) {
            continue;
        }
        const { conditionId, conditionKey, conditionType } = condition;
        // 如果条件满足，则使用该条件的样式覆盖默认样式
        if (
            checkCaseCondition(
                rowData[conditionKey],
                conditionType,
                conditionValueMap[`${conditionId}__$__${conditionKey}`],
            )
        ) {
            return style;
            }
        }
    }
    return contentConfig.defaultStyle ?? {};
}
/**
 * 在模板中渲染字符串的通用处理函数
 *
 * @param val 待渲染的值
 * @param connector 如果为数组时的连接符
 * @returns
 */
export const renderStringInTemplate = (val: any, connector = '-'): any => {
  if (isArray(val)) {
    return val.map((item: any) => renderStringInTemplate(item, connector)).join(connector);
  }
  // 只支持三种基本数据类型
  if (typeof val === 'string' || typeof val === 'number' || typeof val === 'boolean') {
    return val;
  }
  return '';
};

/**
 * 生成应用的basePath
 * @param {String | Number} applicationId
 * @return {String} 根据规则生成的basePath
 */
export function fetchBasePath(applicationId: number, type = 'lowCode') {
    const typeMap: any = {
        adv: 'adv',
        lowCode: 'app',
        pc: 'app',
        h5: 'h5',
    };
    const prefix = typeMap[type.toLocaleLowerCase()] ? typeMap[type.toLocaleLowerCase()] : 'app';
    return `${prefix}_${applicationId}`;
}

// 面包屑点击跳转
export function handleBreadcrumbJump(router, info) {
    const { targetPage, query, applicationId } = info;
    let targetUrl =
        targetPage.settingType === 'CUSTOM_PAGE'
        ? `/${fetchBasePath(applicationId)}/customPage`
        : targetPage.url;
    if (targetUrl.startsWith('app_')) {
        targetUrl = '/' + targetUrl;
    }
    if (targetPage.settingType === 'CUSTOM_PAGE') {
      // 自定义页面将URL推入到query中
        query['iframe_url'] = targetPage.modelId;
    }
    if (targetPage.settingType === 'BI_DASH_BOARD' && isNumber(targetPage.modelId)) {
      // BI预览页面id传递方式采用query，直接在路由中未生效，此处推入到query中
        query['id'] = targetPage.modelId;
    }
    const jumpInfo = {
        path: targetUrl, // 跳转地址
        query,
    };
    const routeUrl = router.resolve(jumpInfo);
    let url = routeUrl.href;
    // 判断是否是MicroApp的子应用
    const microAppData = window.__MICRO_APP_ENVIRONMENT__;
    if (microAppData) {
        const urlList: string[] = [];
        Object.keys(jumpInfo.query).forEach((item) => {
            const urlItem = item + '=' + jumpInfo.query[item];
            urlList.push(urlItem);
        });
        // 判断要跳转的地址是否是用户自定义输入的
        const customJumpPath =
            targetPage.settingType === 'CUSTOM_PAGE' ? jumpInfo.query.iframe_url : jumpInfo.path;
        // 自定义跳转页面不是其他平台的菜单，则直接使用全路径
        if (targetPage.settingType === 'CUSTOM_PAGE' && customJumpPath.indexOf('/allin-app/') === -1) {
            url = jumpInfo.query.iframe_url + '?' + urlList.join('&');
        } else if (customJumpPath.indexOf('/allin-app/') !== -1) {
            // 目标页面是其他平台的菜单
            let jumpPath = customJumpPath.substring(customJumpPath.indexOf('/allin-app/'));
            // 配置有参数，需要加参数添加到路由后面
            if (urlList.length) {
                // 跳转的路径本身带有参数
                if (jumpPath.indexOf('?') !== -1) {
                    jumpPath += '&' + urlList.join('&');
                } else {
                    jumpPath += '?' + urlList.join('&');
                }
            }
            url = location.origin + jumpPath;
        } else {
            // 目标页面就是低代码项目本身的页面
            url = location.origin + '/allin-app' + customJumpPath;
        }
    }
    // 跳转
    if (microAppData) {
        window.location.href = url;
    } else {
        router.push(jumpInfo);
    }
}

// 防抖
export function debounce(fn, wait) {
    let timer = null;
    return function () {
        if (timer) clearTimeout(timer);
        timer = setTimeout(() => {
            timer = null;
            fn.apply(this, arguments);
        }, wait);
    };
}

// 节流
export function throttle(fn, delay) {
    let lastTime = 0;
    return function () {
        let nowDate = Date.now();
        if (nowDate - lastTime > delay) {
            // 如果当前触发时间减去最后一次触发时间大于需要延迟的事件，就执行事件
            lastTime = nowDate;
            fn && fn.apply(this, arguments);
        }
    };
}
export function permission(permissionVal: string | number) {
    const userStore = useUserStoreWithOut()
    const permissionCodeMap: string[] = userStore.getPermissionMap || []
    if (permissionCodeMap.includes(String(permissionVal))) {
      return true
    }
    return false
  }